package org.dreamwork.cai.io.impl;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.dreamwork.cai.SRFException;
import org.dreamwork.cai.core.BroadcastPayload;
import org.dreamwork.cai.core.IHeartbeatManager;
import org.dreamwork.cai.proxy.IServiceResolver;
import org.dreamwork.cai.srf.ServiceRouteFrame;
import org.dreamwork.cai.srf.ServiceRouteTable;
import org.dreamwork.cai.tools.Misc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;

/**
 * 处理路由广播的工具类
 */
public class RouteBroadcastProcessor {
    private static final Logger logger = LoggerFactory.getLogger (RouteBroadcastProcessor.class);

    /**
     * 判断给定的地址和端口是否是环回
     * @param remoteAddress 地址
     * @param senderPort    端口
     * @return 如果是环回地址返回 true，否在 false
     */
    public static boolean isLoopBack (InetSocketAddress remoteAddress, int senderPort) {
        String host = remoteAddress.getAddress ().getHostAddress ();
        int port = remoteAddress.getPort ();
        String key = host + ":" + port;
        if (logger.isTraceEnabled ()) {
            logger.trace ("===============================");
            logger.trace ("= key = {}", key);
            logger.trace ("===============================");

            logger.trace ("receive a message from {}", key);
        }

        if (Misc.isLocalAddress (host) && port == senderPort) {
            if (logger.isTraceEnabled ()) {
                logger.trace ("{}:{} is myself, ignore this message!", host, port);
            }

            return true;
        }

        return false;
    }

    /**
     * 对接收到的广播内容进行处理
     * @param buff          接收到的数据缓冲区
     * @param offset        缓冲区有效数据的偏移量
     * @param length        缓冲区有效数据的长度
     * @param resolver      服务解析器
     * @param remoteAddress 远程地址
     */
    public static void handle (byte[] buff, int offset, int length, IServiceResolver resolver, InetSocketAddress remoteAddress) {
        if (buff != null) {
            String content = new String (buff, offset, length, StandardCharsets.UTF_8);
            if (logger.isTraceEnabled ()) {
                logger.trace ("********************** received raw content **********************");
                logger.trace (content);
                logger.trace ("******************************************************************");
            }
            Gson g = new Gson ();
            BroadcastPayload payload = g.fromJson (content, BroadcastPayload.class);

            String host = remoteAddress.getAddress ().getHostAddress ();
            int port = remoteAddress.getPort ();
            String key = host + ":" + port;
            if (payload.srf != null) {
                payload.srf.host = host;
            }

            if (logger.isTraceEnabled ()) {
                logger.trace ("\r\n=============================================================");
                logger.trace ("received message from {} via {}", key, remoteAddress);
                new GsonBuilder ().setPrettyPrinting ().create ().toJson (payload, System.out);
                System.out.println ();
                logger.trace ("=============================================================");
            }

            IHeartbeatManager heartbeat = resolver.get (IHeartbeatManager.class);
            ServiceRouteTable table     = resolver.get (ServiceRouteTable.class);

            switch (payload.type) {
                case BroadcastPayload.HEARTBEAT:
                    if (logger.isTraceEnabled ()) {
                        logger.trace ("received heartbeat message from {}", key);
                    }
                    // 更新心跳表时间戳
                    heartbeat.touch (host, port);
                    break;

                case BroadcastPayload.WITHDRAWN:
                    if (logger.isTraceEnabled ()) {
                        logger.trace ("the SRF of {} withdrawn", key);
                    }
                    // 从本地路由表中删除已取消的路由帧
                    heartbeat.remove (host);
                    table.removeByHost (host, port);
                    break;

                case BroadcastPayload.UPDATE:
                    if (logger.isTraceEnabled ()) {
                        logger.trace ("the SRF of {} updated", key);
                    }
                    if (payload.srf == null) {
                        logger.warn ("empty srf received, ignore it");
                    } else {
                        try {
                            ServiceRouteFrame cached = table.get (payload.srf.namespace);

                            if (cached != null) {                                   // 如果 srf 曾经缓存过
                                if (cached.host.equals (host)) {                    // 同个节点发送来的 srf
                                    if (cached.timestamp < payload.srf.timestamp) { // 且远程已更新该 srf
                                        table.remove (cached);                      // 删除老的 srf
                                    }
                                }
                            }

                            if (cached == null || cached.timestamp < payload.srf.timestamp) {
                                // 用实际接收的 IP 地址替换 srf 中报告的 host
                                payload.srf.host = host;
                                table.add (payload.srf);
                            }
                        } catch (SRFException ex) {
                            logger.warn (ex.getMessage (), ex);
                        }
                    }
                    break;
            }
        }
    }
}
